Jx.categorize('unit.color', {

	$extend: 'unit.integer',

	$actions: [

		'updateMaskValue',

		'extractMaskValue',

		'computeSets'

	],

	$cascadeCallback: function(Class, Base){

		//-- finalize color bytes
		var proto = Class.prototype,

			sets = proto.sets,

			ranges = proto.ranges,

			sizeIndex = {},

			bitIndex = {},

			setIndex = {};

		if ('bitIndexes' in proto){

			Jx.assign(bitIndex, proto.bitIndexes);

		}

		proto.bitIndexes = bitIndex;

		if ('bitSizes' in proto){

			Jx.assign(sizeIndex, proto.bitSizes);

		}

		proto.bitSizes = sizeIndex;

		proto.setIndexes = setIndex;

		for(var name, range, size, l = sets.length, len = l - 1; l--;){

			name = sets[l];

			range = name in ranges ?

				parseInt(ranges[name], 10) || 0

				:

				(ranges[name] = 127);

			sizeIndex[name] = size = range ?

				proto.$Number.bytes( range + 1) * 4

				:

				0;

			bitIndex[name] = (len - l) * size;

			setIndex[name] = true;

		}

	},

	sets: [],

	ranges: {},

	stripSpaceRe: /[\r\n\t\s ]/g,

	populateHexRe: /([a-f0-9])/g,

	stringProcessors: {

		hex: /^\#?[0-9a-f]+$/,

		func: /^rgb\([0-9]+(\,[0-9]+)*\)$/

	},

	onAfterSetValue: function(operation){

		for(var name, sets = this.sets, l = sets.length; l--;){

			name = sets[l];

			this.updateMaskValue( name,

				this.extractMaskValue(name)

			);

		}

	},

	onUpdateMaskValue: function(operation, name, value){

		//-- extract value
		var bitIndex = this.bitIndexes[name],

			size = this.ranges[name],

			baseValue = this.get('value'),

			oldValue = baseValue >> bitIndex & size;

		this.set(name, value);

		this.set('value',

			baseValue ^ (oldValue << bitIndex) | (value << bitIndex)

		);

	},

	onComputeSet: function(operation){

		var bitIndex = this.bitIndexes,

			ranges = this.ranges,

			values = operation.sets,

			value = 0;

		for(var color, name, sets = this.sets, l = sets.length; l--;){

			color = l in values ?

				parseInt(values[l], 10) || 0

				:

				0;

			value = value | color << bitIndex[ sets[l] ];

		}

		operation.returnValue = value;

	},

	onExtractMaskValue: function(operation, set){

		operation.returnValue = this.get('value') >> this.bitIndexes[set] & this.ranges[set];

	},

	allowUpdateMaskValue: function(operation, name, value){

		return Jx.isString(name, true) &&

			this.setIndexes.hasOwnProperty(name) &&

			Jx.isNumber(value);

	},

	allowComputeSet: function(operation, args){

		var sets = this.sets;

		if (!Jx.isArray(args)){

			args = Array.prototype.slice.call(arguments, 1, this.sets.length);

		}

		//-- check if it has valid arguments
		for(var l = sets.length; l--;){

			if (!Jx.isNumber(sets[l])){

				operation.returnValue = false;

				break;

			}


		}

		operation.sets = args;

		return operation.returnValue;

	},

	allowExtractMaskValue: function(operation, set){

		operation.returnValue = false;

		return Jx.isString(set, true) && this.setIndexes.hasOwnProperty(set);

	},

	allowSetValue: function(operation, value){

		var count = arguments.length,

			found = false;

		//-- multiple value
		if (count == 2){

			if (Jx.isString(value)){

				var result = {

						me: this,

						subject: value.toLowerCase().replace(this.stripSpaceRe, ''),

						found: false

					};

				Jx.each(this.stringProcessors, function(n, v, o){

					var shouldContinue = true;

					if (o.hasOwnProperty(n) &&

						v.test(this.subject)

					){

						this.found = this.me[ n+'Import' ]( this.subject);

						shouldContinue = false;

					}

					return shouldContinue;

				}, result);

				found = result.found;

			} else if (Jx.isNumber(value)){

				found = value;

			}

		} else {

			found = this.computeSet(

				Array.prototype.slice.call(arguments, 1)

			);

		}

		return Jx.isNumber(found) && (operation.value = found);

	},

	isValidColor: function(value, name){

		return Jx.isNumber(value) &&

			value >= 0 &&

			value <= this.ranges[name];

	},

	getValue: function(set){

		return Jx.isString(set, true) && this.setIndexes.hasOwnProperty(set) ?

			this[ 'get' + set.charAt(0).toUpperCase() + set.substring(1, set.length) ]()

			:

			this.$super();

	},

	hexImport: function(str){

		if (str.charAt(0) == '#'){

			str = str.substring(1, str.length);

		}

		if (str.length == 3){

			str = str.replace( this.populateHexRe,'$1$1');

		}

		return parseInt(str, 16);

	},

	funcImport: function(str){

		return this.computeSet(

			str.substring(

				str.indexOf('('),

				str.length - 1

			).split(',')

		);

	},

	toString: function(){
		
		return this.toHex();

	}

});
